`timescale 1ns / 1ps
`include "defines.vh"
//////////////////////////////////////////////////////////////////////////////////
// Company: 
// Engineer: 
// 
// Create Date: 2024/06/14 21:18:09
// Design Name: 
// Module Name: JU
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//////////////////////////////////////////////////////////////////////////////////


//Jump Unit 跳转控制单元
module JU (
    input [0:0] clk,
    input [0:0] rst,

    //由ID_EX传递
    input [`Instruction_Bus] Instruction_Input,
    input [`Instruction_Addr_Bus] Instruction_Addr_Input,

    //ID要读的寄存器的地址
    input [`Reg_Addr_Bus] Reg_1_Read_Addr_Input,
    input [`Reg_Addr_Bus] Reg_2_Read_Addr_Input,


    // 由EX_MEM传递 处理Load-use型冒险
    input [0:0] RAM_Access_Flag_Input,
    input [`RAM_Operation_BUS] RAM_Operation_Input,

    //要写的寄存器的地址
    input [`Reg_Addr_Bus] Write_Reg_Addr_Input,

    // ALU的计算结果
    input [`Reg_Data_Bus] ALU_Result_Input,
    input [0:0] ALU_Carry_Output,
    input [0:0] ALU_Overflow_Output,

    // 由ID_EX传递，指令(判断跳转)
    input [6:0] opcode,
    input [2:0] func3,



    // 锁存器控制
    output reg [0:0] IF_ID_Clear_Flag,
    output reg [0:0] ID_EX_Clear_Flag,
    output reg [0:0] EX_MEM_Clear_Flag,


    //PC地址控制
    output reg [0:0] Jump_Flag,
    output reg [`Instruction_Addr_Bus] Jump_Target_Inst_Addr





);






    always @(*) begin
        IF_ID_Clear_Flag = 0;
        ID_EX_Clear_Flag = 0;
        EX_MEM_Clear_Flag = 0;
        Jump_Flag = 0;
        Jump_Target_Inst_Addr = `Zero32;
        // 注意：EX_MEM_Clear_Flag = 1时，当前指令不会被执行，在冒险处理时使用，而在跳转指令时不使用

        //Load-use型冒险处理
        if( RAM_Access_Flag_Input == `RAM_Access_Flag_Enabled && RAM_Operation_Input[3] == 1'b1) begin  //访存标志为真，且操作为写，此时ALU为访问的RAM地址
            // 清空流水线，重新运行当前指令，相当于插入一条nop指令
            if (Reg_1_Read_Addr_Input == Write_Reg_Addr_Input) begin
                IF_ID_Clear_Flag = 1;
                ID_EX_Clear_Flag = 1;
                EX_MEM_Clear_Flag = 1;
                Jump_Flag = 1;
                Jump_Target_Inst_Addr = Instruction_Addr_Input;
            end else if (Reg_2_Read_Addr_Input == Write_Reg_Addr_Input) begin
                IF_ID_Clear_Flag = 1;
                ID_EX_Clear_Flag = 1;
                EX_MEM_Clear_Flag = 1;
                Jump_Flag = 1;
                Jump_Target_Inst_Addr = Instruction_Addr_Input;
            end
        end

        // 跳转指令处理

        case (opcode)

            //无条件跳转
            `INST_JAL: begin
                IF_ID_Clear_Flag = 1;
                ID_EX_Clear_Flag = 1;
                Jump_Flag = 1;
                Jump_Target_Inst_Addr = ALU_Result_Input;
            end

            `INST_JALR: begin
                IF_ID_Clear_Flag = 1;
                ID_EX_Clear_Flag = 1;
                Jump_Flag = 1;
                Jump_Target_Inst_Addr = ALU_Result_Input;
            end


            //条件跳转
            `INST_TYPE_B: begin
                case (func3)
                    `INST_BEQ: begin
                        if (ALU_Result_Input == `Zero32) begin
                            //跳转的同时进行流水线冲刷   跳转地址 = 原指令地址 + (偏移地址<<1)
                            IF_ID_Clear_Flag = 1;
                            ID_EX_Clear_Flag = 1;

                            Jump_Flag = 1;
                            Jump_Target_Inst_Addr =   Instruction_Addr_Input + {{20{Instruction_Input[31]}},Instruction_Input[7:7],Instruction_Input[30:25],Instruction_Input[11:8],1'b0};
                        end
                    end
                    `INST_BNE: begin
                        if (ALU_Result_Input != `Zero32) begin
                            //跳转的同时进行流水线冲刷   跳转地址 = 原指令地址 + (偏移地址<<1)
                            IF_ID_Clear_Flag = 1;
                            ID_EX_Clear_Flag = 1;

                            Jump_Flag = 1;
                            Jump_Target_Inst_Addr =   Instruction_Addr_Input + {{20{Instruction_Input[31]}},Instruction_Input[7:7],Instruction_Input[30:25],Instruction_Input[11:8],1'b0};
                        end
                    end
                    `INST_BLT: begin
                        if (ALU_Result_Input == 1'b1) begin
                            //跳转的同时进行流水线冲刷   跳转地址 = 原指令地址 + (偏移地址<<1)
                            IF_ID_Clear_Flag = 1;
                            ID_EX_Clear_Flag = 1;

                            Jump_Flag = 1;
                            Jump_Target_Inst_Addr =   Instruction_Addr_Input + {{20{Instruction_Input[31]}},Instruction_Input[7:7],Instruction_Input[30:25],Instruction_Input[11:8],1'b0};
                        end
                    end

                    `INST_BGE: begin
                        if (ALU_Result_Input == 1'b0) begin
                            //跳转的同时进行流水线冲刷   跳转地址 = 原指令地址 + (偏移地址<<1)
                            IF_ID_Clear_Flag = 1;
                            ID_EX_Clear_Flag = 1;

                            Jump_Flag = 1;
                            Jump_Target_Inst_Addr =   Instruction_Addr_Input + {{20{Instruction_Input[31]}},Instruction_Input[7:7],Instruction_Input[30:25],Instruction_Input[11:8],1'b0};
                        end
                    end
                    `INST_BLTU: begin
                        if (ALU_Result_Input == 1'b1) begin
                            //跳转的同时进行流水线冲刷   跳转地址 = 原指令地址 + (偏移地址<<1)
                            IF_ID_Clear_Flag = 1;
                            ID_EX_Clear_Flag = 1;

                            Jump_Flag = 1;
                            Jump_Target_Inst_Addr =   Instruction_Addr_Input + {{20{Instruction_Input[31]}},Instruction_Input[7:7],Instruction_Input[30:25],Instruction_Input[11:8],1'b0};
                        end
                    end
                    `INST_BGEU: begin
                        if (ALU_Result_Input == 1'b0) begin
                            //跳转的同时进行流水线冲刷   跳转地址 = 原指令地址 + (偏移地址<<1)
                            IF_ID_Clear_Flag = 1;
                            ID_EX_Clear_Flag = 1;

                            Jump_Flag = 1;
                            Jump_Target_Inst_Addr =   Instruction_Addr_Input + {{20{Instruction_Input[31]}},Instruction_Input[7:7],Instruction_Input[30:25],Instruction_Input[11:8],1'b0};
                        end
                    end

                endcase
            end
        endcase

        end

        always @(posedge clk) begin
            if (Jump_Flag == 1) begin
                $display($time, "          JU:%d", Jump_Target_Inst_Addr);     
            end



    end











endmodule
